package cn.hzc.fortess.core.aop;

import cn.hutool.core.date.DateUtil;
import cn.hzc.fortess.core.SessionDataHolder;
import cn.hzc.fortess.core.common.HttpContext;
import cn.hzc.fortess.core.common.annotion.CURDLog;
import cn.hzc.fortess.core.common.annotion.CURDType;
import cn.hzc.fortess.core.common.constant.Const;
import cn.hzc.fortess.core.shiro.ShiroKit;
import cn.hzc.fortess.core.shiro.ShiroUser;
import cn.hzc.fortess.system.entity.BaseModel;
import cn.hzc.fortess.system.eventbus.BizLogEvent;
import cn.hzc.fortess.system.eventbus.EventExecute;
import com.google.common.collect.Maps;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

@Aspect
@Component
public class CURDAop {

    private static final Logger log = LoggerFactory.getLogger(CURDAop.class);

    @Pointcut(value = "@annotation(cn.hzc.fortess.core.common.annotion.CURDLog)")
    public void cutService() {
    }

    @Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {

        Object result = point.proceed();

        try {
            handle(point);
        } catch (Exception e) {
            log.error("CURD业务操作日志记录时出现错误:", e);
        }

        return result;
    }

    private void handle(ProceedingJoinPoint point) throws Exception {

        Signature sig = point.getSignature();
        if (!(sig instanceof MethodSignature)) {
            log.warn("@CURDLog的注解只能加在方法上才有效!");
            return;
        }
        final ShiroUser user = ShiroKit.getUser();
        if (null == user) {
            return;
        }


        final MethodSignature msig = (MethodSignature) sig;
        final Object target = point.getTarget();
        final Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        final String methodName = currentMethod.getName();
        String className = point.getTarget().getClass().getName();
        Object[] params = point.getArgs();


        final StringBuilder sb = new StringBuilder();
        if (params != null) {
            for (Object param : params) {
                sb.append(entityToString(param));
                sb.append(" & " + Const.LOG_SPLIT);
            }
        }
        final String methodParam = sb.toString();

        //获取注解
        final CURDLog annotation = currentMethod.getAnnotation(CURDLog.class);
        String bussinessName = annotation.value();
        if (Objects.equals(annotation.curdType(), CURDType.DELETE)) {
            EventExecute.me().post(new BizLogEvent(bussinessName, user.getAccount(),
                    HttpContext.getIp(), methodParam, className, methodName));
            return;
        }
        if (Objects.equals(annotation.curdType(), CURDType.CREATE) || Objects.equals(annotation.curdType(), CURDType.SETTING)) {
            EventExecute.me().post(new BizLogEvent(bussinessName, user.getAccount(),
                    HttpContext.getIp(),
                    "保存的数据:" + Const.LOG_SPLIT + methodParam, className, methodName));
            return;
        }
        final Object object = SessionDataHolder.me().get();
        EventExecute.me().post(new BizLogEvent(bussinessName, user.getAccount(),
                HttpContext.getIp(),
                "原数据:" + Const.LOG_SPLIT +
                        (object == null ? "[no get]" : entityToString(object))
                        + Const.LOG_SPLIT + "修改后:" + Const.LOG_SPLIT + methodParam, className, methodName));


        SessionDataHolder.me().set(null);
    }


    private static Object entityToString(Object pojo1) {

        if (pojo1 != null && pojo1.getClass().isArray()) {
            StringBuilder sb = new StringBuilder("");
            for (Object obj : (Object[]) pojo1) {
                sb.append(obj.toString() + ",");
            }
            return sb.toString();
        }

        if (pojo1 != null && (pojo1 instanceof List)) {
            StringBuilder sb = new StringBuilder("");
            for (Object obj : (List) pojo1) {
                sb.append(obj.toString() + ",");
            }
            return sb.toString();
        }

        if (!(pojo1 instanceof BaseModel)) {
            return pojo1;
        }

        final Map<String, Object> map = Maps.newTreeMap();
        try {
            Class clazz = pojo1.getClass();
            Field[] fields = pojo1.getClass().getDeclaredFields();
            for (Field field : fields) {
                if ("serialVersionUID".equals(field.getName())) {
                    continue;
                }
                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                Method getMethod = pd.getReadMethod();
                Object o1 = getMethod.invoke(pojo1);
                if (o1 == null) {
                    continue;
                }
                if (o1 instanceof Date) {
                    o1 = DateUtil.formatDate((Date) o1);
                }
                map.put(field.getName(), o1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map.toString();
    }

}
